#include "sdkconfig.h"
#ifdef CONFIG_BOARD_SELECT_BAT_PROT_BOARD
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_timer.h"
#include "api_map.h"
#include "dvc.h"
#include "led_strip.h"
#include "board_v1_1_map.h"
#include "esp_log.h"

static const char *TAG = "board";

#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)

extern dvc_t gloab_dvc;
static led_strip_handle_t led_strip = NULL;
struct led_state
{
    uint32_t duty : 7;
    uint32_t dir : 1;
    uint32_t flash : 1;  /* 闪烁模式 */
    uint32_t target : 7; /* 闪烁次数 */
    uint32_t mode : 1;   /* 0:翻转, 1:已入网(渐亮渐灭) */
    uint32_t count : 7;  /* 时间计数 */

    uint32_t aes : 1;
    uint32_t last_aes : 1;
    uint32_t isr : 1;
    uint32_t aes_flash : 1;
    uint32_t aes_count : 4;
} sys_state;

static void ctrl_btn_isr(void *arg)
{
    sys_state.isr = 1;
}

void aes_enable(bool enable)
{
    sys_state.aes = enable;
}

void wifi_network_status_cb(bool status)
{
    sys_state.mode = status;
    if (status)
    {
        sys_state.dir = 0;
        sys_state.duty = 0;
    }
}

static void sys_led_change(uint8_t duty)
{
    // if (ledc_set_duty(LEDC_LOW_SPEED_MODE, 0, duty) != ESP_OK)
    // {
    //     printf("\e[31m[pwm] Failed to set pwm duty\e[0m\n");
    //     return;
    // }
    // if (ledc_update_duty(LEDC_LOW_SPEED_MODE, 0) != ESP_OK)
    // {
    //     printf("\e[31m[pwm] Failed to update pwm duty\e[0m\n");
    //     return;
    // }

    duty = duty * 255 / 100 / 10;
    if (sys_state.mode)
        led_strip_set_pixel(led_strip, 0, 0, 0, duty);
    else
        led_strip_set_pixel(led_strip, 0, duty, 0, 0);
    led_strip_refresh(led_strip);
}

static void timer_cb(void *arg)
{
    if (!sys_state.flash)
    {
        if (sys_state.mode == 1)
        {
            if (!sys_state.duty || sys_state.duty == 100)
                sys_state.dir = !sys_state.dir;
            sys_state.duty += sys_state.dir ? 1 : -1;
            sys_led_change(sys_state.duty);
        }
        else
        {
            sys_state.count++;
            if (sys_state.count == 50) /* 500 ms */
            {
                sys_state.count = 0;
                sys_led_change(sys_state.dir ? 100 : 0);
                sys_state.dir = !sys_state.dir;
            }
        }
    }
    else
    {
        sys_state.count++;
        if (sys_state.count == 20)
        {
            sys_state.count = 0;
            sys_led_change(sys_state.target % 2 ? 100 : 0);
            if (--sys_state.target == 0)
                sys_state.flash = 0;
        }
    }

    if (sys_state.isr)
    {
        if (gpio_get_level(BTN_PIN) == 0)
            sys_state.aes = !sys_state.aes;
    }

    if (sys_state.last_aes != sys_state.aes)
    {
        sys_state.last_aes = sys_state.aes;
        sys_state.aes_count = 0;
        sys_state.aes_flash = 0;
        if (sys_state.isr)
        {
            sys_state.isr = false;
            dvc_aes(gloab_dvc, sys_state.aes ? 1 : 0, DVC_EVENT_FLAG_USER);
        }
        gpio_set_level(AES_EN, sys_state.aes);
        if (!sys_state.aes)
        {
            led_strip_set_pixel(led_strip, 1, 0, 0, 0);
            led_strip_refresh(led_strip);
        }
    }

    if (sys_state.aes)
    {
        sys_state.aes_count++;
        uint8_t max = 15;
        if (sys_state.aes_flash)
            max = 5;
        if (sys_state.aes_count == max)
        {
            sys_state.aes_count = 0;
            sys_state.aes_flash = !sys_state.aes_flash;
            uint8_t duty = sys_state.aes_flash ? 10 : 0;
            led_strip_set_pixel(led_strip, 1, 0, duty, 0);
            led_strip_refresh(led_strip);
        }
    }
}

led_strip_handle_t configure_led(void)
{
    // LED strip general initialization, according to your led board design
    led_strip_config_t strip_config = {
        .strip_gpio_num = SYS_LED,                                   // The GPIO that connected to the LED strip's data line
        .max_leds = LED_COUNT,                                       // The number of LEDs in the strip,
        .led_model = LED_MODEL_WS2812,                               // LED strip model
        .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color order of the strip: GRB
        .flags = {
            .invert_out = false, // don't invert the output signal
        }};

    // LED strip backend configuration: RMT
    led_strip_rmt_config_t rmt_config = {
        .clk_src = RMT_CLK_SRC_DEFAULT,        // different clock source can lead to different power consumption
        .resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
        .mem_block_symbols = 0,                // the memory block size used by the RMT channel
        .flags = {
            .with_dma = 0, // Using DMA can improve performance when driving more LEDs
        }};

    // LED Strip object handle
    led_strip_handle_t led_strip;
    ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
    return led_strip;
}

static void board_auto_init(void)
{
    bool screen_connected = false;
    gpio_config_t io_conf = {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pull_down_en = 1,
        .pull_up_en = 0,
        .pin_bit_mask = (1ULL << AES_EN)};
    gpio_config(&io_conf);
    gpio_set_level(AES_EN, 0);

    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 1;
    io_conf.pin_bit_mask = (1ULL << LCD_SPI_RST_PIN) | (1ULL << LCD_SPI_TE_PIN);
    gpio_config(&io_conf);

    if (!gpio_get_level(LCD_SPI_TE_PIN))
    {
        screen_connected = true;
        ESP_LOGW(TAG, "Screen connected");
    }

    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 1;
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    io_conf.mode = GPIO_MODE_INPUT;
    io_conf.pin_bit_mask = (1ULL << BTN_PIN);
    gpio_config(&io_conf);
    gpio_isr_handler_add(BTN_PIN, ctrl_btn_isr, 0);

    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .timer_num = LEDC_TIMER_0,
        .duty_resolution = 1,
        .freq_hz = 100 * 1000, // 1000 kHz
        .clk_cfg = LEDC_AUTO_CLK};

    if (ledc_timer_config(&ledc_timer) != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to config pwm timer");
        return;
    }

    ledc_channel_config_t ledc_channel = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .timer_sel = LEDC_TIMER_0,
        .channel = (ledc_channel_t)0,
        .gpio_num = AES_PWM,
        .duty = 1,
        .hpoint = 0};
    if (ledc_channel_config(&ledc_channel) != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to config pwm channel0");
        return;
    }

    const esp_timer_create_args_t timer_args = {
        .callback = &timer_cb,
        .name = "sys_state"};
    esp_timer_handle_t sys_state_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&timer_args, &sys_state_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(sys_state_timer, 10 * 1000));

    spi_bus_config_t buscfg = {
        .sclk_io_num = AFE_CLK,
        .mosi_io_num = AFE_MOSI,
        .miso_io_num = AFE_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 64,
    };
    esp_err_t ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
    if (ret)
    {
        ESP_LOGE(TAG, "Failed to initialize SPI0 BUS(%s)", esp_err_to_name(ret));
        return;
    }
    dvc1006_auto_init();

    led_strip = configure_led();
    if (led_strip)
        led_strip_clear(led_strip);
}
ADD_API(board_auto_init)
#endif // CONFIG_BOARD_SELECT_BAT_PROT_BOARD
